﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VulnDBExplorer.Framework.Impl;
using VulnDB.Action.DataCollectors.Services.Impl;
using Iesi.Collections;
using VulnDBExplorer.Services;
using Luna.Framework;

namespace VulnDB.Action.DataCollectors.Actions {
	
	class GenerateCodeDistributionAction: DataCollectorAction {

		public string ProfileData { get; set; }

		public GenerateCodeDistributionAction() {
			Name = this.GetType().Name;
			Text = "Genegate code distribution statistics";

			ProfileData = "codebases.txt";
		}

		protected override void onExecute(object sender, ActionExecutionArgs args) {
			Console.log("Loading CodeProfiler data...");
			CodebaseProfile profile = CodebaseProfile.load(ProfileData, Context);
			Console.log("Calculating statistics...");

			int[,] CI = new int[profile.Codebases.Count, profile.Codebases.Count];

			int filecount = int.MaxValue;
			int skipFile = 0;

			ProgressMonitor = Application.createProgressMonitor();

			ProgressMonitor.start("Calculating CodeProfiler statistics...", Math.Min(filecount, profile.Files.Count));

			try {
				foreach (ProfileEntry entry in profile.Files.Values) {
					if (filecount <= 0) break;
					if (skipFile <= 0) {
						int[,] a = calculateCodeInheritance(profile, entry);
						concat(ref CI, a, profile.Codebases.Count);
						ProgressMonitor.Value++;
						filecount--;
					}
					else
						skipFile--;
					
				}

				// output result
				Console.writeLine("***Code Inheritance***");
				foreach (ICodebase cb in profile.Codebases)
					Console.write("\t{0}", cb.Name);
				Console.writeLine("");

				for (int i = 0; i < profile.Codebases.Count; i++) {
					Console.write(profile.Codebases[i].Name);
					for (int j = 0; j < profile.Codebases.Count; j++) {
						if (j >= i)
							Console.write("\t{0}", CI[i, j]);
						else
							Console.write("\t-");
					}
					Console.writeLine("");
				}
			}
			finally {
				ProgressMonitor.stop();
			}
				

			base.onExecute(sender, args);
		}

		protected void concat(ref int[,] a, int[,] b, int n) {
			for (int i = 0; i < n; i++)
				for (int j = i; j < n; j++)
					a[i, j] += b[i, j];
		}

		protected int[,] calculateCodeInheritance(CodebaseProfile profile, ProfileEntry entry) {
			ISet[,] Sets = new ISet[profile.Codebases.Count, profile.Codebases.Count];
			int[,] CI = new int[profile.Codebases.Count, profile.Codebases.Count];
			for (int i = 0; i < profile.Codebases.Count; i++)
				for (int j = i; j < profile.Codebases.Count; j++) {
					if (i == j) {
						if (i == 0) {
							Sets[i, i] = entry.getUniqueLeftSet(profile.Codebases[i], null);
							CI[i, i] = Sets[i, i].Count;
						}
						else {
							Sets[i, i] = entry.getUniqueRightSet(profile.Codebases[0], profile.Codebases[i]);
							for (int k = 1; k < i; k++)
								Sets[i, i] = Sets[i, i].intersect(entry.getUniqueRightSet(profile.Codebases[k], profile.Codebases[i]));
							CI[i,i] = Sets[i, i].Count;
						}
					}
					else {
						Sets[i, j] = Sets[i, i].intersect(entry.getCommonSet(profile.Codebases[i], profile.Codebases[j]));
						CI[i, j] = Sets[i, j].Count;
					}
				}
			return CI;
		}
	}
}
